AWS Organizations環境でなるべく楽にSSMインベントリデータを集約したい
Systems Manager(SSM)インベントリ はSSM管理下のインスタンスの棚卸しに役立つ機能です。 インスタンスからメタデータを収集して、それらの情報をコンソールから確認できます。 OSやアプリケーション、ネットワーク設定などの情報を収集してくれます。 ※なお、これらの情報はメタデータのみです。機密情報や実データへのアクセスは無いです。
また、 リソースデータの同期 機能を使って、収集したメタデータ(インベントリデータ)を S3バケットへ送信できます。 S3バケットのインベントリデータに対してAmazon AthenaやQuickSight を使って分析ができます。
リソースデータの同期はマルチアカウント対応です。 適切なバケットポリシーを設定することで 1バケットに他アカウントからのインベントリデータを集約できます。 AWS Organizationsにも対応しています。
今回はAWS Organizations環境下で「SSMインベントリデータのS3バケット集約」を、なるべく楽にセットアップすべく、トライしてみました。
2024/06/10追記: 条件キー周りのアップデートのおかげで集約が楽になっています。差分はこちらを参照ください → [小ネタ] aws:SourceOrgID のおかげでSSMインベントリデータのS3集約が楽ちんになっていた | DevelopersIO
セットアップ全体像
インベントリの集約〜活用までの流れは以下のとおりです。
- 集約アカウントにS3バケットを作成する
- 「インベントリ収集」設定をメンバーアカウント群へ展開する
- 「組織ベースのリソースデータ同期」設定をメンバーアカウント群へ展開する
- (オプション) Athenaなど分析ツールを使ってS3バケットに対してクエリを行う
今回は 1.〜3.
までのセットアップ部分が解説範囲です。
セットアップしてみる
(前提) 今回のセットアップ範囲は以下とします。
- 東京リージョンのみ
- 特定OU配下にあるメンバーアカウント群
S3バケット作成〜バケットポリシー 設定
上記S3バケットを作ります。 まずはマネジメントコンソールから、基本デフォルト設定でS3バケットを作成しました。
作成したS3バケットのバケットポリシーを編集します。 以降に示すサンプルポリシーを適宜置き換えます。
DOC-EXAMPLE-BUCKET
を作成したバケット名に置換bucket-prefix/
をバケットプレフィクスに置換 (オプション)organization-id
をAWS Organizationsの組織IDに置換
{ "Version": "2012-10-17", "Statement": [ { "Sid": "SSMBucketPermissionsCheck", "Effect": "Allow", "Principal": { "Service": "ssm.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET" }, { "Sid": " SSMBucketDelivery", "Effect": "Allow", "Principal": { "Service": "ssm.amazonaws.com" }, "Action": "s3:PutObject", "Resource": [ "arn:aws:s3:::DOC-EXAMPLE-BUCKET/bucket-prefix/*/accountid=*/*" ], "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control", "s3:RequestObjectTag/OrgId": "organization-id" } } }, { "Sid": " SSMBucketDeliveryTagging", "Effect": "Allow", "Principal": { "Service": "ssm.amazonaws.com" }, "Action": "s3:PutObjectTagging", "Resource": [ "arn:aws:s3:::DOC-EXAMPLE-BUCKET/bucket-prefix/*/accountid=*/*" ] } ] }
今回は bucket-prefix/
は default/
としました。
※「AWS Organizationsの組織ID」は管理アカウントのOrganizationsコンソールから確認できます。
SSMインベントリ収集 設定
上記「インベントリ収集」設定をしていきます。 この設定には、 SSM 高速セットアップ が便利なので使っていきます。 高速セットアップはSSM周辺の設定を簡単に適用できる機能です。
※この設定は AWS Organizations の管理アカウント 上で行います。
マネジメントコンソールから [高速セットアップ] を選択して、 Host Management [作成] を選択します。
以降でオプションを指定していきます。
設定オプションでは最低限「インベントリ収集」設定はONにします。 その他は用途に合わせて、ついでにONにしても(しなくても)構いません。
次にターゲットを選択します。 今回は東京リージョンのみの前提なので「カスタム」を選択します。 ターゲットのOU、リージョンを指定しましょう。
インスタンスプロファイルのオプションはONにしても(しなくても)構いません。
問題なければ [作成] を選択しましょう。
これで「インベントリ収集」設定は完了です。
リソースデータ同期 設定
最後に「リソースデータ同期」設定を行います。
これは高速セットアップでは設定できません。 なおかつ今(2023/01)時点では、「組織ベースのリソースデータ同期」は CloudFormationのResourceDataSyncリソースでは作成できません。
なので、この設定は CLI(SDK)で実施する必要があります。
全アカウントに同じ設定を投入することを考えて、今回は Lambda-backed カスタムリソース を活用します。 これは簡潔に言うと、CloudFormation展開時に 指定したLambda関数を実行できるリソース(機能)です。 ※詳細は「参考」章の記載リンクをご覧ください。
以下のようなCloudFormationテンプレートを作成しました。 「組織ベースのリソースデータ同期」を作成する Lambda-backed カスタムリソースを記述しています。
AWSTemplateFormatVersion: "2010-09-09" Parameters: BucketName: Type: String BucketPrefix: Type: String Default: default SyncName: Type: String Default: org-sync RoleName: Type: String Default: LambdaSetupRole-SSMResourceDataSync Resources: SSMResourceDataSync: Type: Custom::SSMResourceDataSync Properties: ServiceToken: !GetAtt "LambdaFunction.Arn" BucketName: !Ref BucketName BucketPrefix: !Ref BucketPrefix SyncName: !Ref SyncName ### Lambda function and role for Create/Delete ResourceDataSync LambdaFunction: Type: AWS::Lambda::Function Properties: Role: !GetAtt "LambdaExecutionRole.Arn" Runtime: "python3.9" Handler: index.lambda_handler Timeout: 60 Code: ZipFile: | import boto3 import cfnresponse from logging import getLogger, INFO, log logger = getLogger() logger.setLevel(INFO) def lambda_handler(event, context): logger.info('[START] lambda_handler') bucket_name = event['ResourceProperties']['BucketName'] bucket_prefix = event['ResourceProperties']['BucketPrefix'] sync_name = event['ResourceProperties']['SyncName'] logger.info(f'BucketName:{bucket_name}') logger.info(f'BucketPrefix:{bucket_prefix}') logger.info(f'SyncName:{sync_name}') if event['RequestType'] == 'Create': logger.info('running ssm.create_resource_data_sync') try: ssm = boto3.client('ssm') ssm.create_resource_data_sync( SyncName=sync_name, S3Destination={ 'BucketName': bucket_name, 'Prefix': bucket_prefix, 'SyncFormat': 'JsonSerDe', 'Region': 'ap-northeast-1', 'DestinationDataSharing': { 'DestinationDataSharingType': 'Organization' } } ) except Exception as e: logger.error(e) cfnresponse.send(event, context, cfnresponse.FAILED, {'Response': 'Failure'}) exit() else: cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'}) if event['RequestType'] == 'Update': pass cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'}) if event['RequestType'] == 'Delete': logger.info('running ssm.delete_resource_data_sync') try: ssm = boto3.client('ssm') ssm.delete_resource_data_sync( SyncName=sync_name ) except Exception as e: logger.error(e) cfnresponse.send(event, context, cfnresponse.FAILED, {'Response': 'Failure'}) exit() else: cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'}) logger.info('[END] lambda_handler') LambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Ref RoleName AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: "lambda.amazonaws.com" Action: "sts:AssumeRole" ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" Policies: - PolicyName: SSMCreateResourceDataSync PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "ssm:CreateResourceDataSync" - "ssm:DeleteResourceDataSync" Resource: "*"
管理アカウントもしくはCloudFormation委任先アカウントから、このテンプレートをStackSetsとして展開しましょう。 ※組織単位でStackSets を展開するための詳しい手順は省きます。「参考」章記載のリンクをご覧ください。
以下パラメータを指定します。
BucketName
: (集約先S3バケットの名前)BucketPrefix
: (集約先S3バケットのプレフィクス)SyncName
: (リソースデータ同期の名前)RoleName
: (Lambda関数の実行ロール名)
高速セットアップで指定したOU、リージョンと同じターゲットを指定して、展開します。 スタックインスタンスのステータスが全て「SUCCEEDED」となればOKです。
確認
test.json
StackSets展開が成功している場合、S3バケットの (prefix)/AWS:InstanceInformation/accountid=(各アカウントID)/test.json
というファイルができているはずです。念のため確認しておきましょう。
EC2インスタンスを作成してみる
メンバーアカウント上にSSM管理下のEC2インスタンスを1つ作成してみます。
集約S3バケットのあるアカウントへログインして確認すると、以下JSONが格納されていました。
(プレフィクス)/AWS:AWSComponent/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:Application/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:BillingInfo/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:ComplianceItem/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:ComplianceSummary/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:InstanceDetailedInformation/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:InstanceInformation/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:Network/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json (プレフィクス)/AWS:Tag/accountid=(アカウントID)/region=ap-northeast-1/resourcetype=ManagedInstanceInventory/(インスタンスID).json
1つピックアップして見てみます。 以下キャプチャは AWS:Application
のJSONです。 該当インスタンスにインストールされているアプリケーション情報が格納されています。
{ "ApplicationType": "System Environment/Libraries", "InstalledTime": "2022-12-15T21:53:40Z", "Architecture": "x86_64", "Version": "1.0.4", "Summary": "String library, very low memory overhead, simple to import", "PackageId": "ustr-1.0.4-16.amzn2.0.3.src.rpm", "Publisher": "Amazon Linux", "Release": "16.amzn2.0.3", "URL": "http://www.and.org/ustr/", "Name": "ustr", "resourceId": "i-0ac46e13720305980", "captureTime": "2023-01-10T02:39:10Z", "schemaVersion": "1.1" } { "ApplicationType": "System Environment/Base", "InstalledTime": "2022-12-15T21:53:27Z", "Architecture": "x86_64", "Version": "2", "Epoch": "1", "Summary": "Amazon Linux release files", "PackageId": "system-release-2-14.amzn2.src.rpm", "Publisher": "Amazon Linux", "Release": "14.amzn2", "URL": "https://amazonlinux.com/", "Name": "system-release", "resourceId": "i-0ac46e13720305980", "captureTime": "2023-01-10T02:39:10Z", "schemaVersion": "1.1" } # (略)
それぞれの内容について詳細または分析の深堀りは本ブログでは割愛します。
補足や注意点
「組織ベースのリソースデータ同期」の作成制限について
「組織ベースのリソースデータ同期」は1つのみ作成できます(1リージョン ✕ 1アカウント単位)。 既に「組織ベースのリソースデータ同期」を作っている場合、 紹介したテンプレートは展開に失敗します。
You can only have one organization-based resource data sync.
– Configuring resource data sync for Inventory - AWS Systems Manager
リソースデータ同期の特性について
これはOrganizations関係なく、リソースデータ同期の特性の話です。
以下のような挙動でS3へ同期されます。
- マネージドノードを削除しても、削除されたノードのインベントリファイルは維持される
- 実行中のノードが更新された場合、インベントリファイルは自動的に上書きされる
なので S3にあるインベントリデータ = 現在アクティブなインスタンス
ではない ことに注意してください。
アクティブかどうかは AWS:InstanceInformation テーブルから確認できます。
おわりに
SSMインベントリデータのS3バケット集約を試してみました。
リソースデータ同期の部分はブログ投稿時点では少し工夫が必要ですが、 それ以外は比較的容易にセットアップできるかと思います。
実際に格納されているデータの内容や分析周りは以下公式ドキュメントが参考になります。 機会があれば実際に試してブログ化もしたいです。
- Metadata collected by inventory - AWS Systems Manager
- Walkthrough: Use resource data sync to aggregate inventory data - AWS Systems Manager
以上、参考になれば幸いです。
参考
- Configuring resource data sync for Inventory - AWS Systems Manager
- create-resource-data-sync — AWS CLI 2.9.13 Command Reference
- SSM — Boto3 Docs 1.26.37 documentation
- Lambda-backed カスタムリソース
- CloudFormation StackSets